﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Collections.ObjectModel;
using System.Diagnostics;
using Ppt = Microsoft.Office.Interop.PowerPoint;
using Microsoft.Office.Tools;
using MVVM.Core;
using ObjectModel.PowerPoint.New;
using ObjectModel.Utilities;
using ViewComponents.PowerPoint;
using ViewComponents.Services;

namespace ViewComponents.PowerPoint.ViewModels
{
    public class LinkedTextRangesViewModel : ViewModelBase, IPPTViewModel
    {
        #region Declarations

        readonly Ppt.Presentation mPres;
        readonly IWindowService mWindowService;
        readonly IDialogService mDialogService;
        readonly PPTViewModelController mController;
        readonly LinkedSlideManager mSlideManager;
        LinkedSlide mActiveSlide;
        LinkedTextRange mSelectedTextRange;
        ICommand mAddCommand;
        ICommand mRemoveCommand;
        ICommand mUpdateCommand;
        ICommand mUpdateAllCommand;
        ICommand mShowConstantsCommand;

        #endregion

        #region Properties

        public Array SourceObjectTypes
        {
            get { return Enum.GetValues(typeof(SourceObjectType)); }
        }

        public ObservableCollection<Constant> Constants { get; private set; }

        bool mTextIsSelected;
        public bool TextIsSelected
        {
            get { return mTextIsSelected; }
            set
            {
                if (mTextIsSelected == value) return;
                mTextIsSelected = value;
                OnPropertyChanged("TextIsSelected");
            }
        }

        public LinkedSlide ActiveSlide
        {
            get { return mActiveSlide; }
            set
            {
                if (mActiveSlide == value) return;
                mActiveSlide = value;
                OnPropertyChanged("ActiveSlide");
            }
        }

        public LinkedTextRange ActiveTextRange
        {
            get { return mSelectedTextRange; }
            set
            {
                if (mSelectedTextRange == null)
                    TempSourceObject = null;

                if (mSelectedTextRange == value) return;
                mSelectedTextRange = value;

                // Select the text range in PowerPoint.
                if (mSelectedTextRange != null)
                {
                    mController.Disable();
                    mSelectedTextRange.Select();
                    mController.Enable();
                }

                //ActiveSourceObject = value == null ? null : value.Source;
                TempSourceObject = value == null ? null : value.Source.Copy();
                OnPropertyChanged("ActiveTextRange");
            }
        }

        //public SourceObject ActiveSourceObject
        //{
        //    get
        //    {
        //        return mActiveSourceObject ?? (mActiveSourceObject = 
        //            SourceObjectFactory.CreateObject((SourceObjectType)ActiveSourceObjectType));
        //    }

        //    private set
        //    {
        //        mActiveSourceObject = value;
        //        ActiveSourceObjectType = GetSourceObjectType(value);
        //        OnPropertyChanged("ActiveSourceObject");
        //    }
        //}

        //public SourceObjectType? ActiveSourceObjectType
        //{
        //    get
        //    {
        //        return mActiveSourceObjectType;
        //    }

        //    set
        //    {
        //        mActiveSourceObjectType = value;
        //        OnPropertyChanged("ActiveSourceObjectType");
        //    }
        //}

        SourceObject mTempSourceObject;
        public SourceObject TempSourceObject
        {
            get { return mTempSourceObject; }

            set
            {
                mTempSourceObject = value;
                TempSourceObjectType = GetSourceObjectType(value);
                OnPropertyChanged("TempSourceObject");
            }
        }

        SourceObjectType? mTempSourceObjectType;
        public SourceObjectType? TempSourceObjectType
        {
            get { return mTempSourceObjectType; }

            set
            {
                mTempSourceObjectType = value;
                OnPropertyChanged("TempSourceObjectType");
            }
        }

        #endregion

        #region Constructors

        public LinkedTextRangesViewModel(Ppt.Presentation pres, IWindowService windowService, IDialogService dialogService)
        {
            mPres = pres;
            mWindowService = windowService;
            mDialogService = dialogService;

            Constants = ObjectModel.PowerPoint.Globals.PresentationConstants[pres].Constants;
            CollectionItemListener<ObservableCollection<Constant>> listener =
                new CollectionItemListener<ObservableCollection<Constant>>(Constants);
            listener.ItemPropertyChanged += constants_ItemPropertyChanged;

            mSlideManager = new LinkedSlideManager(pres);
            
            mController = new PPTViewModelController(pres, mSlideManager.Slides, this);
            mController.BeforeTextRangeDeleted += BeforeTextRangeDeleted;
        }

        void constants_ItemPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Value")
            {
                Constant c = (Constant)sender;
                foreach (LinkedSlide ls in mSlideManager.Slides.Values)
                {
                    foreach (LinkedTextRange ltr in ls.TextRanges)
                    {
                        if (ltr.Source is ConstantSource)
                        {
                            ConstantSource source = (ConstantSource)ltr.Source;
                            if (source.Constant == c)
                                ltr.UpdateValue();
                        }
                    }
                }
            }
        }

        #endregion

        #region Factory Methods

        public static ViewModelBase CreateInstance(Ppt.Presentation pres)
        {
            IWindowService windowService = new BasicWindowService();
            IDialogService dialogService = new MessageBoxDialogService();

            return new LinkedTextRangesViewModel(pres, windowService, dialogService);
        }

        #endregion

        #region Commands

        ICommand mAddSourceCommand;
        public ICommand AddSourceCommand
        {
            get { return mAddSourceCommand ?? (mAddSourceCommand = new ViewModelCommand(() => { }, CanAddSourceExecute, this)); }
        }

        ICommand mAddConstantCommand;
        public ICommand AddConstantCommand
        {
            get { return mAddConstantCommand ?? (mAddConstantCommand = new ViewModelCommand(AddConstantExecute)); }
        }

        ICommand mAddExcelSourceCommand;
        public ICommand AddExcelSourceCommand
        {
            get { return mAddExcelSourceCommand ?? (mAddExcelSourceCommand = new ViewModelCommand(AddExcelSourceExecute)); }
        }

        public ICommand AddCommand
        {
            get { return mAddCommand ?? (mAddCommand = new ViewModelCommand(AddExecute)); }
        }

        public ICommand RemoveCommand
        {
            get { return mRemoveCommand ?? (mRemoveCommand = new ViewModelCommand(RemoveExecute, CanRemoveExecute, this)); }
        }

        public ICommand UpdateCommand
        {
            get { return mUpdateCommand ?? (mUpdateCommand = new ViewModelCommand(UpdateExecute, CanUpdateExecute, this)); }
        }

        public ICommand UpdateAllCommand
        {
            get { return mUpdateAllCommand ?? (mUpdateAllCommand = new ViewModelCommand(UpdateAllExecute, CanUpdateAllExecute, this)); }
        }

        public ICommand ShowConstantsCommand
        {
            get { return mShowConstantsCommand ?? (mShowConstantsCommand = new ViewModelCommand(ShowConstantsExecute)); }
        }

        #endregion

        #region Command Methods

        bool CanAddSourceExecute()
        {
            return TextIsSelected;
        }

        void AddConstantExecute()
        {
            TempSourceObject = SourceObjectFactory.CreateObject(SourceObjectType.Constant);
        }

        void AddExcelSourceExecute()
        {
            TempSourceObject = SourceObjectFactory.CreateObject(SourceObjectType.ExcelRange);
        }

        //void AddExecute()
        //{
        //    LinkedTextRange linkedTextRange = LinkedTextRangeFactory.CreateFromSelection();
        //    if (linkedTextRange != null)
        //    {
        //        linkedTextRange.Source = SourceObjectFactory.CreateObject(SourceObjectType.Constant);
        //        AddLinkedTextRange(linkedTextRange);
        //    }
        //}

        void AddExecute()
        {
            if (ActiveTextRange == null)
            {
                LinkedTextRange ltr = LinkedTextRangeFactory.CreateFromSelection();
                if (ltr != null)
                {
                    ltr.Source = TempSourceObject.Copy();
                    AddLinkedTextRange(ltr);
                }
            }
            else
                ActiveTextRange.Source = TempSourceObject;

            UpdateExecute();
        }

        bool CanRemoveExecute()
        {
            return ActiveTextRange != null;
        }

        void RemoveExecute()
        {
            RemoveLinkedTextRange(ActiveTextRange);
        }

        bool CanUpdateExecute()
        {
            return ActiveTextRange != null;
        }

        void UpdateExecute()
        {
            if (ActiveTextRange != null)
            {
                // Updating a text range triggers a TextSelectionChanged
                // event so in order to control this, the view model
                // controller must be temporarily disabled;
                mController.Disable();
                ActiveTextRange.UpdateValue();
                ActiveTextRange.Select();
                mController.Enable();
            }
        }

        [DebuggerStepThrough]
        bool CanUpdateAllExecute()
        {
            return ActiveSlide.TextRanges.Count > 0;
        }

        void UpdateAllExecute()
        {
            // Updating a text range triggers a TextSelectionChanged
            // event so in order to control this, the view model
            // controller must be temporarily disabled;
            mController.Disable();
            foreach (LinkedTextRange ltr in ActiveSlide.TextRanges)
                ltr.UpdateValue();
            if (ActiveTextRange != null) ActiveTextRange.Select();
            mController.Enable();
        }

        void ShowConstantsExecute()
        {
            mWindowService.ShowViewModelInDialog<ConstantsManagerWindow>
                (new ConstantsViewModel(mWindowService, mDialogService));
        }

        #endregion

        #region Methods

        void AddLinkedTextRange(LinkedTextRange linkedTextRange)
        {
            ActiveSlide.TextRanges.Add(linkedTextRange);
            ActiveTextRange = linkedTextRange;
        }

        void RemoveLinkedTextRange(LinkedTextRange linkedTextRange)
        {
            ActiveSlide.TextRanges.Remove(linkedTextRange);
        }

        void BeforeTextRangeDeleted(object sender, BeforeTextRangeDeletedEventArgs e)
        {
            string prefix = e.IsDelayed ? "A previous" : "This";
            string msg = string.Format("{0} action has deleted part of a text range which is linked" +
                      " to a data source.\n\nWould you like to remove this link from its associated" +
                      " text range?", prefix);
            e.Cancel = mDialogService.ShowMessage(msg, "Remove Linked Source",
                DialogButton.YesNo, DialogImage.Exclamation) == DialogResponse.No;
        }

        //SourceObjectType? GetSourceObjectType(SourceObject o)
        //{
        //    if (o is ConstantSource)
        //        return SourceObjectType.Constant;
        //    else
        //        return null;
        //}

        SourceObjectType? GetSourceObjectType(SourceObject o)
        {
            if (o is ConstantSource)
                return SourceObjectType.Constant;
            else
                return null;
        }

        #endregion
    }
}
